package e.odbo.data.service.lock.impl;

import com.openbravo.data.basic.BasicException;
import e.odbo.data.bean.key.KeyBuilder;
import e.odbo.data.sample.lock.OdbOLock;
import e.odbo.data.sample.lock.OdbOLockDAO;
import e.odbo.data.service.lock.I_LockService;
import e.odbo.data.service.lock.LockedListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.UUID;

public class OdbOLockService implements I_LockService {
    public static final Logger logger= LoggerFactory.getLogger(OdbOLockService.class);

    public static final String TAG=OdbOLockService.class.getName();

    OdbOLockDAO odbOLockDAO;

    //重试次数
    private int retryLockCount =10;

    //锁超时时长(毫秒)
    private long lockedTimeOut =3000;

    private String lockOwner;

    public OdbOLockService(OdbOLockDAO odbOLockDAO) {
        this.odbOLockDAO = odbOLockDAO;
        lockOwner= UUID.randomUUID().toString();
    }


    public OdbOLockService(OdbOLockDAO odbOLockDAO, int retryLockCount, long lockedTimeOut) {
        this.odbOLockDAO = odbOLockDAO;
        this.retryLockCount = retryLockCount;
        this.lockedTimeOut = lockedTimeOut;
        lockOwner= UUID.randomUUID().toString();
    }


    protected OdbOLock checkLock(String id) throws BasicException {
        OdbOLock lock=odbOLockDAO.find(KeyBuilder.getKey(id));
        if(lock==null) {
            lock=new OdbOLock(id);
            odbOLockDAO.insert(lock);
        }
        return lock;
    }

    protected OdbOLock preLock(String id)throws BasicException {
        OdbOLock lock=checkLock(id);
        if(lock.getLockOwner()!=null) {
            if(isLockedTimeOut(lock.getLockTime(), lockedTimeOut)){
                logger.info(TAG," db lock get a time out lock at:"+id);
                return lock;
            }
            return null;
        }
        return lock;
    }

    public boolean lock(String id)throws BasicException{
        OdbOLock lock;
        int tryLock= retryLockCount;
        while (tryLock>0){
            lock=preLock(id);
            if(lock!=null){
                lock.setLockOwner(lockOwner);
                lock.setLockTime(new Date());
                odbOLockDAO.update(lock);
                return true;
            }
            tryLock--;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    @Override
    public void lock(String id, LockedListener listener) {
        while(true){
            try {
                if(lock(id)){
                    listener.locked();
                    unLock(id);
                    return;
                }
            } catch (BasicException e) {
                listener.onError(e);
                break;
            }
        }
    }

    public void unLock(String id)throws BasicException{
        OdbOLock lock=checkLock(id);
        if(!lockOwner.equalsIgnoreCase(lock.getLockOwner())) {
            logger.info(TAG,"unlock with lock owner check error :"+id);
            return;
        }
        Date lockTime=lock.getLockTime();
        lock.setLockOwner(null);
        lock.setLockTime(null);
        odbOLockDAO.update(lock);
        if(isLockedTimeOut(lockTime,lockedTimeOut))
            logger.info(TAG," lock time out error :"+id+"\tlocked time:"+(new Date().getTime()-lockTime.getTime()));
    }

    private boolean isLockedTimeOut(Date lockedTime, long timeOut){
        Date now=new Date();
        if(now.getTime()-lockedTime.getTime()>timeOut)
            return true;
        return false;
    }
}
